﻿using System;
using System.Linq.Expressions;

namespace Lilium.LParse
{
    public static class ParserMonad
    {
        public static Parser<TToken, TResult2> Select<TToken, TResult1, TResult2>(
            this Parser<TToken, TResult1> parser, 
            Func<TResult1, TResult2> selector)
        {
            return state =>
            {
                var result1 = parser(state);
                if (state.Failed)
                    return default(TResult2);
                else
                    return selector(result1);
            };
        }

        public static Parser<TToken, TResult2> SelectMany<TToken, TResult1, TResult2>(
            this Parser<TToken, TResult1> parser,
            Func<TResult1, Parser<TToken, TResult2>> selector)
        {
            return state =>
            {
                var result1 = parser(state);
                if (!state.Failed)
                    return selector(result1)(state);
                else
                    return default(TResult2);
            };
        }

        public static Parser<TToken, TResult3> SelectMany<TToken, TResult1, TResult2, TResult3>(
            this  Parser<TToken, TResult1> parser,
            Func<TResult1, Parser<TToken, TResult2>> selector,
            Func<TResult1, TResult2, TResult3> projector)
        {
            return state =>
            {
                var result1 = parser(state);
                
                if (state.Failed)
                    return default(TResult3);

                var result2 = selector(result1)(state);

                if (state.Failed)
                    return default(TResult3);

                return projector(result1, result2);
            };
        }

        public static Parser<TToken, TResult> Where<TToken, TResult>(this Parser<TToken, TResult> parser, Func<TResult, bool> predicate)
        {
            return ParserCombinators.Satisfy(parser, predicate);
        }
    }
}